'use strict';

/**
 * 文件用途说明 : 聊天室服务端处理
 *
 * 项目名称 : nodejs-war
 * 作者姓名 : crixusshen
 * 创建日期 : 16/2/14
 * 修改日期 : 16/2/14
 * 版权所有 : 东方证券股份有限公司
 **/
var socketio = require('socket.io');
var io;
var guestNumber = 1;
var nickNames = {};
var namesUsed = [];
var currentRoom = {};

exports.listen = function (server) {
    io = socketio.listen(server);
    io.set('log level', 1);
    // 连接处理函数
    io.sockets.on('connection', function (socket) {

        // 定义辅助函数：分配昵称、房间更换请求、昵称更换请求、发送聊天信息、房间创建、用户断开连接

        // 分配昵称
        guestNumber = assignGuestName(socket, guestNumber, nickNames, namesUsed);
        // 用户连接上来时把他放入房间Lobby里
        joinRoom(socket, 'Lobby');
        // 处理用户的消息，更名，以及聊天室的创建和变更
        handleMessageBroadcasting(socket, nickNames);
        handleNameChangeAttempts(socket, nickNames, namesUsed);
        handleRoomJoining(socket);

        // 用户发出请求时，向其提供已经被占用的聊天室的列表
        socket.on('rooms', function () {
            var rooms = [];
            for(var p in currentRoom){
                var v = currentRoom[p];
                if(rooms.indexOf(v) == -1){
                    rooms.push(v);
                }
            }
            socket.emit('rooms', rooms);
        });

        // 定义用户断开连接后的清除
        handleClientDisconnection(socket, nickNames, namesUsed);
    });
};

/**
 * 分配用户昵称
 * @param socket        当前连接
 * @param guestNumber   系统生成昵称后的数字（计数器）,由于是demo这里不考虑多线程和并发操作
 * @param nickNames     当前昵称集合
 * @param namesUsed     被占用昵称集合
 */
function assignGuestName(socket, guestNumber, nickNames, namesUsed){
    // 生成新昵称
    var name = 'Guest' + guestNumber;
    // 把用户昵称跟客户端连接ID关联起来
    nickNames[socket.id] = name;
    // 让用户知道他们的昵称
    socket.emit('nameResult', {
        success: true,
        name: name
    });
    // 存放已经被占用的昵称
    namesUsed.push(name);
    // 增加用来生成昵称的计数器
    return guestNumber + 1;
}

/**
 * 加入聊天室
 * @param socket        当前连接
 * @param room          房间名称
 */
function joinRoom(socket, room){
    // 让用户进入房间
    socket.join(room);
    // 记录用户的当前房间
    currentRoom[socket.id] = room;
    // 让用户知道他们进入的新房间
    socket.emit('joinResult', {
        room: room
    });
    // 让房间里的其他用户知道有新用户进入了此房间
    socket.to(room).emit('message', {
        text: nickNames[socket.id] + ' has joined ' + room + '.'
    });

    //// 如果不止一个用户在这个房间里，汇总下都是谁
    //var usersInRoom = io.clients(room); // clients:Gets a list of clients.
    //// 确定有哪些用户在这个房间内
    //if(usersInRoom.length > 1){
    //    var usersInRoomSummary = 'Users currently in ' + room + ':';
    //    for(var index in usersInRoom){
    //        var userSocketId = usersInRoom[index].id;
    //        if(userSocketId != socket.id){
    //            if(index > 0){
    //                usersInRoomSummary += ', ';
    //            }
    //            usersInRoomSummary += nickNames[userSocketId];
    //        }
    //    }
    //    usersInRoomSummary += '.';
    //
    //    // 将房间内其他用户的汇总发送给该用户
    //    // TODO 第二版考虑将房间内的用户显示在右侧『用户列表』区域
    //    socket.emit('message', {
    //        text: usersInRoomSummary
    //    });
    //}
}

/**
 * 处理更名请求（服务端添加监听：nameAttempt）
 * @param socket        当前连接
 * @param nickNames     当前昵称集合
 * @param namesUsed     被占用昵称集合
 */
function handleNameChangeAttempts(socket, nickNames, namesUsed){
    // 添加nameAttempt监听事件
    socket.on('nameAttempt', function (name) {
        // 昵称不能以Guest开头
        if(name.indexOf('Guest') == 0){
            socket.emit('nameResult', {
                success: false,
                message: 'Names cannot begin with "Guest".'
            });
        } else {
            // 如果昵称还没注册就注册上
            if(namesUsed.indexOf(name) == -1){
                var previousName = nickNames[socket.id];
                var previousNameIndex = namesUsed.indexOf(previousName);
                namesUsed.push(name);
                nickNames[socket.id] = name;
                // 删除之前用的昵称，让其他用户可以使用
                delete namesUsed[previousNameIndex];

                socket.emit('nameResult', {
                    success: true,
                    name: name
                });

                // 告诉房间内其他人我修改了昵称
                socket.to(currentRoom[socket.id]).emit('message', {
                    text: previousName + ' is now known as ' + name + '.'
                });
            }
            // 如果昵称已占用，向客户端发送错误信息
            else {
                socket.emit('nameResult', {
                    success: false,
                    message: 'That name is already in use.'
                });
            }
        }
    });
}

/**
 * 发送聊天消息
 * @param socket    当前连接
 */
function handleMessageBroadcasting(socket){
    // 增加消息接受监听器,同时还需要向房间内其他用户发送消息
    socket.on('message', function (message) {
        socket.to(message.room).emit('message', {
            text: nickNames[socket.id] + ':' + message.text
        });
    });
}

/**
 * 创建房间
 * @param socket    当前连接
 */
function handleRoomJoining(socket){
    socket.on('join', function (room) {
        // 离开当前房间
        socket.leave(currentRoom[socket.id]);
        // 进入新的房间
        joinRoom(socket, room.newRoom);
    });
}

/**
 * 用户断开连接
 * @param socket        当前连接
 * @param nickNames     同上
 * @param namesUsed     同上
 */
function handleClientDisconnection(socket, nickNames, namesUsed){
    socket.on('disconnect', function () {
        // 清除临时变量定义
        var nameIndex = namesUsed.indexOf(nickNames[socket.id]);
        delete namesUsed[nameIndex];
        delete nickNames[socket.id];
    });
}